#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <semaphore.h>

using namespace std;

// 默认容量
const int gCap = 10;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gCap)
        : ringqueue_(cap), pIndex_(0), cIndex_(0)
    {
        // 生产(空间信号量)
        sem_init(&roomSem_, 0, ringqueue_.size());
        
        // 消费(数据信号量)
        sem_init(&dataSem_, 0, 0);

        pthread_mutex_init(&pmutex_ ,nullptr);
        pthread_mutex_init(&cmutex_ ,nullptr);
    }

    // 生产 -- 先申请信号量
    void push(const T &in)
    {
        // 申请信号量在锁前面的话，如果是多线程，那么多个线程都可以申请到资源
        // 然后再去争锁
        sem_wait(&roomSem_); // 如果锁加在前面的话，信号量就无法被多次的申请(P操作)

        // 在锁这里等时，每个线程都是拿着信号量去等
        pthread_mutex_lock(&pmutex_);

        ringqueue_[pIndex_] = in; // 生产的过程，有线程安全的问题
        pIndex_++;   // 写入位置后移
        pIndex_ %= ringqueue_.size(); // 更新下标，保证环形特征

        pthread_mutex_unlock(&pmutex_);
        sem_post(&dataSem_); // V操作
    }

    // 消费
    T pop()
    {
        sem_wait(&dataSem_); // 申请数据资源
        pthread_mutex_lock(&cmutex_);

        T temp = ringqueue_[cIndex_]; // 消费
        cIndex_++;
        cIndex_ %= ringqueue_.size(); // 更新下标，保证环形特征

        pthread_mutex_unlock(&cmutex_);
        sem_post(&roomSem_); // 数据已拿走，空间就露出来了，空间多了一个

        return temp;
    }

    ~RingQueue()
    {
        // 销毁信号量计数器
        sem_destroy(&roomSem_);
        sem_destroy(&dataSem_);

        pthread_mutex_destroy(&pmutex_);
        pthread_mutex_destroy(&cmutex_);
    }
private:
    vector<T> ringqueue_; // 环形队列
    sem_t roomSem_;       // 衡量空间计数器，productor
    sem_t dataSem_;       // 衡量数据计数器，consumer
    uint32_t pIndex_;     // 当前生产者写入的位置，如果是多线程，pIndex_也是临界资源
    uint32_t cIndex_;     // 当前消费者读取的位置，如果是多线程，cIndex_也是临界资源

    pthread_mutex_t pmutex_;
    pthread_mutex_t cmutex_;
};
